"""
策略参数优化框架
支持多参数组合的自动优化
"""
from dataclasses import dataclass
from typing import Dict, List, Tuple, Optional
from datetime import datetime
import json
from src.utils.logger import get_logger

logger = get_logger(__name__)


@dataclass
class StrategyParams:
    """策略参数定义"""
    # EMA参数
    ema_fast_period: int = 9
    ema_slow_period: int = 21
    ema_signal_period: int = 5

    # RSI参数
    rsi_period: int = 14
    rsi_overbought: float = 70
    rsi_oversold: float = 30

    # MACD参数
    macd_fast: int = 12
    macd_slow: int = 26
    macd_signal: int = 9

    # 布林带参数
    bb_period: int = 20
    bb_std: float = 2.0

    # 交易参数
    stop_loss_pct: float = 0.01  # 1%
    take_profit_pct: float = 0.02  # 2%
    max_position_bars: int = 20

    # 时间框架权重
    tf_4h_weight: float = 0.4
    tf_1h_weight: float = 0.3
    tf_15m_weight: float = 0.2
    tf_5m_weight: float = 0.1

    # 信号阈值
    signal_threshold: float = 0.6  # 信心度阈值

    def to_dict(self) -> Dict:
        """转换为字典"""
        return {
            'ema_fast': self.ema_fast_period,
            'ema_slow': self.ema_slow_period,
            'ema_signal': self.ema_signal_period,
            'rsi_period': self.rsi_period,
            'rsi_overbought': self.rsi_overbought,
            'rsi_oversold': self.rsi_oversold,
            'macd_fast': self.macd_fast,
            'macd_slow': self.macd_slow,
            'macd_signal': self.macd_signal,
            'bb_period': self.bb_period,
            'bb_std': self.bb_std,
            'stop_loss': self.stop_loss_pct,
            'take_profit': self.take_profit_pct,
            'max_position_bars': self.max_position_bars,
            'signal_threshold': self.signal_threshold,
        }

    @classmethod
    def from_dict(cls, data: Dict) -> 'StrategyParams':
        """从字典创建"""
        return cls(
            ema_fast_period=data.get('ema_fast', 9),
            ema_slow_period=data.get('ema_slow', 21),
            ema_signal_period=data.get('ema_signal', 5),
            rsi_period=data.get('rsi_period', 14),
            rsi_overbought=data.get('rsi_overbought', 70),
            rsi_oversold=data.get('rsi_oversold', 30),
            macd_fast=data.get('macd_fast', 12),
            macd_slow=data.get('macd_slow', 26),
            macd_signal=data.get('macd_signal', 9),
            bb_period=data.get('bb_period', 20),
            bb_std=data.get('bb_std', 2.0),
            stop_loss_pct=data.get('stop_loss', 0.01),
            take_profit_pct=data.get('take_profit', 0.02),
            max_position_bars=data.get('max_position_bars', 20),
            signal_threshold=data.get('signal_threshold', 0.6),
        )


@dataclass
class OptimizationResult:
    """优化结果"""
    params: StrategyParams
    total_return: float  # 总收益率
    win_rate: float  # 胜率
    sharpe_ratio: float  # 夏普比率
    max_drawdown: float  # 最大回撤
    profit_factor: float  # 盈利因子
    trades_count: int  # 交易笔数

    def score(self) -> float:
        """计算综合分数 (用于排序)"""
        # 权重配置
        weights = {
            'total_return': 0.4,
            'win_rate': 0.2,
            'sharpe_ratio': 0.2,
            'max_drawdown': -0.1,  # 负数表示回撤越小越好
            'profit_factor': 0.1,
        }

        score = (
            weights['total_return'] * self.total_return +
            weights['win_rate'] * self.win_rate +
            weights['sharpe_ratio'] * min(self.sharpe_ratio, 3.0) / 3.0 +  # 归一化
            weights['max_drawdown'] * self.max_drawdown +
            weights['profit_factor'] * min(self.profit_factor, 3.0) / 3.0
        )

        return score

    def __str__(self) -> str:
        return (
            f"收益率: {self.total_return:.2%} | "
            f"胜率: {self.win_rate:.1%} | "
            f"夏普: {self.sharpe_ratio:.2f} | "
            f"回撤: {self.max_drawdown:.2%} | "
            f"因子: {self.profit_factor:.2f} | "
            f"分数: {self.score():.3f}"
        )


class ParameterOptimizer:
    """参数优化器"""

    def __init__(self):
        self.results: List[OptimizationResult] = []
        self.best_params: Optional[StrategyParams] = None
        self.best_result: Optional[OptimizationResult] = None

    def generate_parameter_combinations(self,
                                       ema_fast_range: Tuple[int, int, int] = (5, 15, 2),
                                       ema_slow_range: Tuple[int, int, int] = (15, 35, 3),
                                       rsi_period_range: Tuple[int, int, int] = (10, 20, 2),
                                       stop_loss_range: Tuple[float, float, float] = (0.005, 0.015, 0.002)
                                       ) -> List[StrategyParams]:
        """
        生成参数组合

        Args:
            ema_fast_range: (start, stop, step)
            ema_slow_range: (start, stop, step)
            rsi_period_range: (start, stop, step)
            stop_loss_range: (start, stop, step)

        Returns:
            参数组合列表
        """
        combinations = []

        # 生成EMA快线参数范围
        ema_fast_values = range(ema_fast_range[0], ema_fast_range[1], ema_fast_range[2])
        # 生成EMA慢线参数范围 (必须 > 快线)
        ema_slow_values = range(ema_slow_range[0], ema_slow_range[1], ema_slow_range[2])
        # 生成RSI参数范围
        rsi_values = range(rsi_period_range[0], rsi_period_range[1], rsi_period_range[2])
        # 生成止损参数范围
        stop_loss_values = []
        current = stop_loss_range[0]
        while current <= stop_loss_range[1]:
            stop_loss_values.append(round(current, 4))
            current += stop_loss_range[2]

        # 生成所有组合
        total_combinations = len(list(ema_fast_values)) * len(list(ema_slow_values)) * \
                           len(list(rsi_values)) * len(stop_loss_values)

        logger.info(f"将生成 {total_combinations} 个参数组合进行优化")

        count = 0
        for ema_fast in ema_fast_values:
            for ema_slow in ema_slow_values:
                if ema_slow <= ema_fast:  # EMA慢线必须 > 快线
                    continue
                for rsi in rsi_values:
                    for stop_loss in stop_loss_values:
                        params = StrategyParams(
                            ema_fast_period=ema_fast,
                            ema_slow_period=ema_slow,
                            rsi_period=rsi,
                            stop_loss_pct=stop_loss,
                            take_profit_pct=stop_loss * 2,  # 止盈为止损的2倍
                        )
                        combinations.append(params)
                        count += 1

        logger.info(f"实际生成了 {count} 个有效的参数组合")
        return combinations

    def add_result(self, result: OptimizationResult):
        """添加优化结果"""
        self.results.append(result)

        # 更新最优参数
        if self.best_result is None or result.score() > self.best_result.score():
            self.best_result = result
            self.best_params = result.params

    def get_best_params(self, top_n: int = 10) -> List[OptimizationResult]:
        """获取前N个最优参数"""
        sorted_results = sorted(self.results, key=lambda x: x.score(), reverse=True)
        return sorted_results[:top_n]

    def save_results(self, filename: str = "optimization_results.json"):
        """保存优化结果"""
        results_dict = []

        for result in self.get_best_params(100):
            results_dict.append({
                'params': result.params.to_dict(),
                'total_return': result.total_return,
                'win_rate': result.win_rate,
                'sharpe_ratio': result.sharpe_ratio,
                'max_drawdown': result.max_drawdown,
                'profit_factor': result.profit_factor,
                'trades_count': result.trades_count,
                'score': result.score(),
            })

        with open(filename, 'w') as f:
            json.dump(results_dict, f, indent=2)

        logger.info(f"优化结果已保存到 {filename}")

    def print_summary(self):
        """打印优化总结"""
        if not self.best_result:
            logger.warning("没有优化结果")
            return

        print("\n" + "="*80)
        print("参数优化完成")
        print("="*80)

        print("\n最优参数:")
        print(f"  EMA快线周期: {self.best_params.ema_fast_period}")
        print(f"  EMA慢线周期: {self.best_params.ema_slow_period}")
        print(f"  RSI周期: {self.best_params.rsi_period}")
        print(f"  止损: {self.best_params.stop_loss_pct:.2%}")
        print(f"  止盈: {self.best_params.take_profit_pct:.2%}")

        print("\n最优结果:")
        print(f"  {self.best_result}")

        print("\n前10个最优参数组合:")
        for i, result in enumerate(self.get_best_params(10), 1):
            print(f"  {i}. {result}")


class AdaptiveParameterAdjuster:
    """自适应参数调整器 - 根据市场状况动态调整参数"""

    def __init__(self, base_params: StrategyParams):
        self.base_params = base_params
        self.current_params = base_params
        self.market_volatility = 0.0
        self.recent_win_rate = 0.5

    def calculate_market_volatility(self, df, period: int = 20) -> float:
        """计算市场波动率"""
        returns = df['close'].pct_change().dropna()
        volatility = returns.rolling(window=period).std().iloc[-1]
        return volatility

    def adjust_parameters(self, df) -> StrategyParams:
        """
        根据市场状况调整参数

        低波动市场: 使用更快的EMA (快速响应)
        高波动市场: 使用更慢的EMA (过滤噪音)
        """
        volatility = self.calculate_market_volatility(df)
        self.market_volatility = volatility

        adjusted_params = StrategyParams(
            ema_fast_period=self.base_params.ema_fast_period,
            ema_slow_period=self.base_params.ema_slow_period,
            rsi_period=self.base_params.rsi_period,
            stop_loss_pct=self.base_params.stop_loss_pct,
            take_profit_pct=self.base_params.take_profit_pct,
        )

        if volatility < 0.005:  # 低波动
            # 使用更快的EMA
            adjusted_params.ema_fast_period = max(3, self.base_params.ema_fast_period - 2)
            adjusted_params.ema_slow_period = max(10, self.base_params.ema_slow_period - 3)
            # 放宽RSI阈值，增加交易机会
            adjusted_params.rsi_overbought = 75
            adjusted_params.rsi_oversold = 25

        elif volatility > 0.02:  # 高波动
            # 使用更慢的EMA，过滤噪音
            adjusted_params.ema_fast_period = min(20, self.base_params.ema_fast_period + 3)
            adjusted_params.ema_slow_period = min(50, self.base_params.ema_slow_period + 5)
            # 收紧RSI阈值，提高信号质量
            adjusted_params.rsi_overbought = 65
            adjusted_params.rsi_oversold = 35
            # 增加止损距离，避免被止损
            adjusted_params.stop_loss_pct = self.base_params.stop_loss_pct * 1.5

        self.current_params = adjusted_params

        logger.debug(
            f"市场波动率: {volatility:.4f}, "
            f"调整EMA: {adjusted_params.ema_fast_period}/"
            f"{adjusted_params.ema_slow_period}"
        )

        return adjusted_params

    def get_current_params(self) -> StrategyParams:
        """获取当前参数"""
        return self.current_params
